package dy.com.第7章服务器优化.Client;

import dy.com.收发并行.Util.ByteUtils;
import dy.com.收发并行.constants.UDPConstants;
import dy.com.第7章服务器优化.Client.bean.ServerInfo;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class UDPClientSearcher {

    static public ServerInfo searchServer(int searchPort, int timeOut) {
        //创建接收消息的CountDownlaunch
        CountDownLatch receiveCountDownLatch = new CountDownLatch(1);
        //开启监听
        Listener listener = null;
        try {
            //开启监听
            listener = listen(receiveCountDownLatch, searchPort);
            //广播
            broadcast();
            //等待监听结束
            receiveCountDownLatch.await();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }

        System.out.println("UDP searcher 已经完成");
        //监听失败
        if (listener == null) return null;
        //获取设备信息,并且结束监听
        List<ServerInfo> devices = listener.getServerAndClose();
        ServerInfo res = devices.get(0);
        return res;
    }

    private static Listener listen(CountDownLatch reveiveCountDownLatch, int searchPort) throws InterruptedException {
        System.out.println("进入listen()函数，开始监听");
        //用于 当监听线程开始后再继续主线程
        CountDownLatch startCountDownLatch = new CountDownLatch(1);

        Listener listener = new Listener(reveiveCountDownLatch, startCountDownLatch, searchPort);
        //监听线程开始运行
        listener.start();
        ////等监听线程开始run后再继续主线程
        startCountDownLatch.await();
        return listener;
    }

    private static void broadcast() throws IOException {
        System.out.println("开始广播");
        DatagramSocket ds = new DatagramSocket();

        byte[] buffer = new byte[256];

        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
        byteBuffer.put(UDPConstants.HEADER);
        byteBuffer.putShort((short)1);
        byteBuffer.putInt(UDPConstants.PORT_CLIENT_RESPONSE);

        DatagramPacket dp = new DatagramPacket(buffer,byteBuffer.position(), InetAddress.getByName("255.255.255.255"), UDPConstants.UDP_PORT_SERVER);
        ds.send(dp);
        ds.close();
        System.out.println("广播结束");
    }

    private static class Listener extends Thread {
        List<ServerInfo> devices = null;
        //用于判断接收信息完成的countdownlatch
        CountDownLatch reveiveCountDownLatch;
        CountDownLatch startCountDownLatch;
        boolean finish;
        int searchPort;
        DatagramSocket ds = null;
        DatagramPacket dp = null;
        byte[] buffer = null;

        Listener(CountDownLatch reveiveCountDownLatch, CountDownLatch startCountDownLatch, int searchPort) {
            this.startCountDownLatch = startCountDownLatch;
            this.reveiveCountDownLatch = reveiveCountDownLatch;
            this.searchPort = searchPort;
            devices = new LinkedList<>();
            finish = false;
        }

        @Override
        public void run() {
            System.out.println("监听线程已经开始run");
            startCountDownLatch.countDown();

            try {
                ds = new DatagramSocket(searchPort);
                buffer = new byte[256];
                //构建监听实体
                dp = new DatagramPacket(buffer, buffer.length);

                while (!finish) {
                    //接收
                    ds.receive(dp);

                    //打印udp服务端的信息
                    String serverUdpIp = dp.getAddress().getHostAddress();
                    int serverUdpPort = dp.getPort();
                    int dataLen = dp.getLength();
                    byte[] data = dp.getData();
                    int minLen = UDPConstants.HEADER.length + 2 + 4;
                    boolean valid = dataLen >= minLen && ByteUtils.startsWith(data, UDPConstants.HEADER);
                    System.out.println("接收到来自:" + serverUdpIp + ":" + serverUdpPort + "的信息，valid:" + valid);
                    if (!valid) continue;

                    ByteBuffer byteBuffer = ByteBuffer.wrap(data, UDPConstants.HEADER.length, dataLen);
                    short cmd = byteBuffer.getShort();
                    int tcpPort = byteBuffer.getInt();
                    //不合法
                    if (cmd != 2 || tcpPort <= 0) {
                        System.out.println("UDPSearcher receive cmd:" + cmd + ",serverPort:" + tcpPort);
                        continue;
                    }
                    String sn = new String(data, minLen, dataLen - minLen);
                    ServerInfo si = new ServerInfo(serverUdpIp, tcpPort, sn);
                    devices.add(si);
                    reveiveCountDownLatch.countDown();
                }


            } catch (IOException e) {
               // e.printStackTrace();
            } finally {
                close();
            }
            System.out.println("子线程监听结束");
        }

        //关闭ds
        private void close() {
            if (ds != null) {
                ds.close();

                ds = null;
            }
        }

        private List<ServerInfo> getServerAndClose() {
            finish = true;
            close();
            return devices;
        }
    }
}
